package com.bsj.power.system.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.http.HttpRequest;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.bsj.power.common.config.cached.BindCardCached;
import com.bsj.power.common.config.cached.LocationDeviceCached;
import com.bsj.power.common.config.cached.PathCached;
import com.bsj.power.common.config.cached.RedisCached;
import com.bsj.power.common.config.global.JsonResult;
import com.bsj.power.common.config.global.JsonResultEnum;
import com.bsj.power.common.config.redis.RedisConstant;
import com.bsj.power.common.constant.ApiConstants;
import com.bsj.power.common.constant.NumericalConstants;
import com.bsj.power.common.constant.UWBPosConstant;
import com.bsj.power.common.def.dto.cardReport.BindCardDTO;
import com.bsj.power.common.def.entity.HighRise;
import com.bsj.power.common.def.entity.Scene;
import com.bsj.power.common.def.entity.gb28181.Device;
import com.bsj.power.common.def.entity.job.Job;
import com.bsj.power.common.def.entity.job.JobPersonnel;
import com.bsj.power.common.def.entity.job.WorkPersonnel;
import com.bsj.power.common.def.entity.locationDevice.LocationDevice;
import com.bsj.power.common.def.entity.personnel.Personnel;
import com.bsj.power.common.def.entity.room.Room;
import com.bsj.power.common.def.vo.homePage.DataStatisticsVO;
import com.bsj.power.common.def.vo.homePage.JobLinkCountVO;
import com.bsj.power.common.def.vo.pls.AlarmReportVO;
import com.bsj.power.common.def.vo.pls.DeviceTypeVO;
import com.bsj.power.common.def.vo.pls.WorkPersonnelVO;
import com.bsj.power.common.def.vo.pls.httpVo.HttpTagPosVO;
import com.bsj.power.common.def.vo.web.job.JobVO;
import com.bsj.power.common.def.vo.web.job.home.TotalNumDay;
import com.bsj.power.common.mapper.*;
import com.bsj.power.common.util.DateUtils;
import com.bsj.power.common.util.FindMediaDevUtils;
import com.bsj.power.system.config.open.OpenApiConfig;
import com.bsj.power.system.service.HomePageService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import redis.clients.bsj.JedisClusterBSJ;

import javax.annotation.Resource;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.temporal.ChronoUnit;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author LiJiaXin
 * @version 1.0.0
 * @description HomePageServiceImpl
 * @time 2024/6/7 10:32
 */
@Slf4j
@Service
public class HomePageServiceImpl implements HomePageService {

    @Value("${cvrIds}")
    private String cvrIds;

    @Resource
    private JobMapper jobMapper;

    @Resource
    private CardReportMapper cardReportMapper;

    @Resource
    private AlarmReportMapper alarmReportMapper;

    @Autowired
    private PathCached pathCached;

    @Autowired
    private LocationDeviceCached locationDeviceCached;

    @Resource
    private DeviceMapper deviceMapper;

    @Resource
    private JobPersonnelMapper jobPersonnelMapper;

    @Resource
    private SceneMapper sceneMapper;

    @Resource
    private HighRiseMapper highRiseMapper;

    @Autowired
    private RedisCached redisCached;

    @Autowired
    private BindCardCached bindCardCached;

    @Autowired
    private OpenApiConfig openApiConfig;

    @Resource
    private PersonnelMapper personnelMapper;

    @Resource
    private RoomMapper roomMapper;

    @Autowired
    private JedisClusterBSJ jedisClusterBSJ;

    @Resource
    private PathReportMapper pathReportMapper;

    public static void main(String[] args) {
        //        Date ago = DateUtils.nDaysAgo(LocalDateTime.now(), 6);
//        System.out.println(DateUtils.dateToStr(ago));
//        Date date = new Date();
//        Date agoStart = DateUtils.getStartOfDay(ago);
//        Date nowEnd = DateUtils.getEndOfDay(date);
//        System.out.println(DateUtils.dateToStr(agoStart));
//        System.out.println(DateUtils.dateToStr(nowEnd));
//
//        //近7日作业统计
//        List<String> dateAfter = DateUtils.getDateAfter(ago, date);
//        dateAfter.forEach(System.out::println);

    }

    /**
     * 数据统计
     *
     * @return com.bsj.power.common.def.vo.homePage.DataStatisticsVO
     * @author ljx
     * @time 2024/6/7 11:10
     */
    @Override
    public DataStatisticsVO dataStatistics() {
        Date date = new Date();
        //近7天
        Date ago = DateUtils.nDaysAgo(LocalDateTime.now(), 6);
        DataStatisticsVO dataStatisticsVO = new DataStatisticsVO();
        dataStatisticsVO.setPathTotal(pathCached.hlenPathInfo());

        Date startOfDay = DateUtils.getStartOfDay(date);
        Date endOfDay = DateUtils.getEndOfDay(date);
        String startTime = DateUtils.getStartOfDay2(date);
        String endTime = DateUtils.getEndOfDay2(date);
        //进区域人数（当天只要进了现场有发送位置数据就代表进区域）
        long enterAreaPersonTotal = 0L;
        List<LocationDevice> locationDevices = locationDeviceCached.hgetAllLocationDeviceInfo();
        if (!CollectionUtils.isEmpty(locationDevices)) {
            for (LocationDevice locationDevice : locationDevices) {
                if (locationDevice.getDeviceType() != NumericalConstants.ONE) {
                    //位置信息
                    String str = redisCached.get(UWBPosConstant.REDIS_TAG_POS_KEY + locationDevice.getDeviceName());
                    if (StringUtils.isNotBlank(str)) {
                        HttpTagPosVO httpTagPosVO = JSONObject.parseObject(str, HttpTagPosVO.class);
                        long time = httpTagPosVO.getTime();
                        //只计算当天
                        if (startOfDay.getTime() <= time && endOfDay.getTime() >= time) {
                            //当天进过的都参与计算
                            enterAreaPersonTotal++;
                        }
                    }
                }
            }
        }
        dataStatisticsVO.setEnterAreaPersonTotal(enterAreaPersonTotal);
        Map<String, Long> map = pathReportMapper.groupCountByType(startTime, endTime);
        dataStatisticsVO.setEnterPersonTotal(map.get("enterPersonTotal"));
        dataStatisticsVO.setGetOutPersonTotal(map.get("getOutPersonTotal"));
        //报警类型分布
        dataStatisticsVO.setAlarmTypeCountVOS(alarmReportMapper.groupCountByAlarmType(startTime, endTime));
        //设备类型分布
        Map<Integer, Long> deviceTypeMap = getDeviceStatistics();
        if (!CollectionUtils.isEmpty(deviceTypeMap)) {
            List<DeviceTypeVO> deviceTypeVOS = new LinkedList<>();
            deviceTypeMap.entrySet().forEach(typeMap -> {
                deviceTypeVOS.add(new DeviceTypeVO(deviceTypeToStr(typeMap.getKey()), typeMap.getValue()));

            });
            dataStatisticsVO.setDeviceTypeVOS(deviceTypeVOS);
            Long num = deviceTypeMap.get(NumericalConstants.FOUR);
            dataStatisticsVO.setMonitorTotal(num == null ? NumericalConstants.ZERO : num);
        }
        List<Long> ids = Arrays.stream(cvrIds.split(",")).map(Long::parseLong).collect(Collectors.toList());
        Map<Integer, Map<Integer, Long>> videoTypeNums = deviceMapper.selectByTypeCount(ids);
        List<DeviceTypeVO> videoDeviceTypes = new LinkedList<>();
        //默认类型都要返回
        for (int i = 1; i < 5; i++) {
            Map<Integer, Long> vtMap = videoTypeNums.get(i);
            Long videoNum = vtMap == null ? 0L : vtMap.get("total");
            videoDeviceTypes.add(new DeviceTypeVO(videoDeviceTypeToStr(i), videoNum));
        }
        dataStatisticsVO.setVideoTypeNums(videoDeviceTypes);
        //安全运行天数
        dataStatisticsVO.setSafeDays(getSafeDays());
        //作业
        Date agoStart = DateUtils.getStartOfDay(ago);
        Date nowStart = DateUtils.getStartOfDay(date);
        Date nowEnd = DateUtils.getEndOfDay(date);
        //近7天工单作业集合
        List<Job> jobs = jobMapper.selectList(Wrappers.<Job>lambdaQuery().between(Job::getStartTime, agoStart, nowEnd));
        //当日工单集合
        List<Job> nowJobs = jobs.stream().filter(item -> item.getStartTime().getTime() >= nowStart.getTime() && item.getStartTime().getTime() <= nowEnd.getTime()).collect(Collectors.toList());
        int nowJobSize = nowJobs.size();
        //当日工单数量
        dataStatisticsVO.setJobTotal((long) nowJobSize);
//        dataStatisticsVO.setJobTotal(jedisClusterBSJ.hlen(RedisConstant.JOB_ORDER_INFO_KEY));
//        long riskCount = nowJobs.stream().filter(item -> item.getEndTime() != null
//                && item.getStartTime().getTime() >= nowStart.getTime()
//                && item.getEndTime().getTime() <= nowEnd.getTime() && !item.getRiskLevel().equals(NumericalConstants.ONE)).count();
        long riskCount = nowJobs.stream().filter(item -> item.getStartTime().getTime() >= nowStart.getTime()
                && item.getStartTime().getTime() <= nowEnd.getTime() && !item.getRiskLevel().equals(NumericalConstants.ONE)).count();
        //中高风险工单数量
        dataStatisticsVO.setTodayJobTotal(riskCount);

        long executeTotal = 0L;
        //执行率改为工单下有作业人员就代表已执行
        for (Job nowJob : nowJobs) {
            if (jobPersonnelMapper.exists(Wrappers.<JobPersonnel>lambdaQuery().eq(JobPersonnel::getJobId, nowJob.getJobId()))) {
                executeTotal++;
            }
        }
        //执行率
        Double nowRate = 0D;
        //当前天已完成的作业数
//        long completeCount = nowJobs.stream().filter(item -> item.getEndTime() != null
//                && item.getStartTime().getTime() >= nowStart.getTime()
//                && item.getEndTime().getTime() <= nowEnd.getTime()
//                && item.getStatus().equals(NumericalConstants.TWO)).count();
        //计算今日执行率
        if (nowJobSize > 0 && executeTotal > 0) {
            nowRate = NumberUtil.div(executeTotal, nowJobSize, 2);
        }
        dataStatisticsVO.setJobRate(Double.parseDouble(String.format("%.2f", nowRate)));
        //当日作业明细，低风险不需要
        dataStatisticsVO.setNowJobs(nowJobs.stream().filter(job -> !job.getRiskLevel().equals(NumericalConstants.ONE)).collect(Collectors.toList()));
        //近7日作业统计
        List<String> dateAfter = DateUtils.getDateAfter(ago, date);
        List<TotalNumDay> totalNumDays = new ArrayList<>();
        Map<String, Long> countByDay = jobs.stream().collect(Collectors.groupingBy(item -> (DateUtils.dateToStr(DateUtils.yearMonthDay, item.getStartTime())), Collectors.counting()));
        for (int i = 0; i < dateAfter.size(); i++) {
            String day = dateAfter.get(i);
            Long dayCount = countByDay.get(day);
            if (dayCount == null) {
                dayCount = 0L;
            }
            totalNumDays.add(TotalNumDay.builder()
                    .time(day)
                    .total(dayCount).build());
        }
//        countByDay.forEach((dateTime, count) -> {
//            TotalNumDay totalNumDay = TotalNumDay.builder().time(DateUtils.dateToStr(yearMonthDay, new Date(dateTime * 1000L * 60 * 60 * 24))).total(count).build();
//            totalNumDays.add(totalNumDay);
//        });
        dataStatisticsVO.setTotalNumDays(totalNumDays);
        return dataStatisticsVO;
    }

    /**
     * 设备类型转换
     *
     * @param deviceType
     * @return java.lang.Integer
     * @author ljx
     * @time 2024/7/20 11:34
     */
    private String deviceTypeToStr(Integer deviceType) {
        String result;
        switch (deviceType) {
            case 1:
                result = "基站设备";
                break;
            case 2:
                result = "UWB设备";
                break;
            case 3:
                result = "UWB+北斗设备";
                break;
            case 4:
                result = "视频设备";
                break;
            default:
                result = "未知";
                break;
        }
        return result;
    }

    /**
     * 转换视频设备类型
     *
     * @param deviceType
     * @return
     */
    private String videoDeviceTypeToStr(Integer deviceType) {
        String result;
        switch (deviceType) {
            case 1:
                result = "球形摄像头";
                break;
            case 2:
                result = "AI球形摄像头";
                break;
            case 3:
                result = "网络摄像头";
                break;
            case 4:
                result = "防爆球形摄像头";
                break;
            default:
                result = "未知";
                break;
        }
        return result;
    }

    /**
     * 获取安全运行天数
     *
     * @return java.lang.Long
     * @author ljx
     * @time 2024/7/8 16:43
     */
    private Long getSafeDays() {
        long daysBetween = NumericalConstants.ZERO;
        String str = redisCached.get(RedisConstant.SAFE_DAYS_BEGIN_TIME);
        if (StringUtils.isNotBlank(str)) {
            Date beginTime = JSON.parseObject(str, Date.class);
            LocalDate beginLocalDate = beginTime.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
            LocalDate localDate = new Date().toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
            daysBetween = ChronoUnit.DAYS.between(beginLocalDate, localDate);
        }
        return daysBetween;
    }

    /**
     * 报警信息
     *
     * @param alarmType
     * @returnjava.util.List < com.bsj.power.common.def.vo.pls.AlarmReportVO>
     * @author ljx
     * @time 2024/6/11 14:00
     */
    @Override
    public List<AlarmReportVO> alarmInfo(Integer alarmType) {
        Date date = new Date();
        String startTime = DateUtils.getStartOfDay2(date);
        String endTime = DateUtils.getEndOfDay2(date);
        return alarmReportMapper.listAlarmInfo(alarmType, startTime, endTime);
    }

    /**
     * 获取统计数据
     *
     * @return java.util.List<com.bsj.power.common.def.vo.pls.DeviceTypeVO>
     * @author ljx
     * @time 2024/6/11 17:19
     */
    @Override
    public List<DeviceTypeVO> getStatisticsData() {
        List<DeviceTypeVO> deviceTypeVOS = new ArrayList<>();
        Map<Integer, Long> map = getDeviceStatistics();
        if (!CollectionUtils.isEmpty(map)) {
            long locationTotal = map.get(NumericalConstants.TWO) != null ? map.get(NumericalConstants.TWO) : 0L;
            locationTotal += map.get(NumericalConstants.THREE) != null ? map.get(NumericalConstants.THREE) : 0L;
            // TODO 作业区域数暂定当天工单数
//            List<Job> jobs = jobMapper.selectList(Wrappers.<Job>lambdaQuery().orderByDesc(Job::getStartTime));
            Date date = new Date();
            String start = DateUtils.getStartOfDay2(date);
            String end = DateUtils.getEndOfDay2(date);
            Long jobTotal = jobMapper.selectCount(Wrappers.<Job>lambdaQuery().between(Job::getStartTime, start, end));
//            Map<String, Long> workAreaMap = jobs.stream().filter(job -> StringUtils.isNotBlank(job.getWorkArea())).collect(Collectors.groupingBy(Job::getWorkArea, Collectors.counting()));
            deviceTypeVOS = List.of(new DeviceTypeVO("基站设备数", map.get(NumericalConstants.ONE)),
                    new DeviceTypeVO("定位设备数", locationTotal),
                    new DeviceTypeVO("视频设备数", map.get(NumericalConstants.FOUR)),
                    new DeviceTypeVO("作业区域数", jobTotal));
        }
        return deviceTypeVOS;
    }

    /**
     * 获取天气信息
     *
     * @param
     * @return com.alibaba.fastjson2.JSONObject
     * @author ljx
     * @time 2024/6/17 15:04
     */
    @Override
    public JSONObject getWeatherInfo() {
        String result = HttpRequest.get(openApiConfig.syncJobUrl + ApiConstants.GET_WEATHER_INFO).execute().body();
        log.info("天气接口地址：{}", openApiConfig.syncJobUrl + ApiConstants.GET_WEATHER_INFO);
        log.info("天气返回结果：{}", result);
        if (StringUtils.isNotBlank(result)) {
            JSONObject jsonObject = JSON.parseObject(result);
            Integer status = jsonObject.getInteger("status");
            if (status != null && status.equals(JsonResultEnum.SUCCESS.getCode())) {
                JSONArray jsonArray = jsonObject.getJSONArray("data");
                if (!CollectionUtils.isEmpty(jsonArray)) {
                    Object obj = jsonArray.get(NumericalConstants.ZERO);
                    if (obj != null) {
                        return (JSONObject) obj;
                    }
                }
            }
        }
        return new JSONObject();
    }

    /**
     * 设置时间为安全运行天数起始时间
     *
     * @param date
     * @return com.bsj.power.common.config.global.JsonResult
     * @author ljx
     * @time 2024/7/8 16:25
     */
    @Override
    public JsonResult resetTime(String date) {
        redisCached.set(RedisConstant.SAFE_DAYS_BEGIN_TIME, DateUtils.strToDate(date));
        return JsonResult.success();
    }

    /**
     * 获取工单关联的人员数据统计
     *
     * @param jobId
     * @return
     */
    @Override
    public JsonResult<JobLinkCountVO> queryJobLinkCount(Long jobId) {
        Job job = jobMapper.selectById(jobId);
        if (job == null) {
            return JsonResult.success();
        }
        JobLinkCountVO jobLinkCountVO = new JobLinkCountVO();
        List<WorkPersonnel> personnelLists = jobPersonnelMapper.selectJobIdByPersonnelList(jobId);
        JobVO jobVO = new JobVO();
        BeanUtil.copyProperties(job, jobVO);
        //实际作业人员信息
//        Map<String, WorkPersonnel> nowPersonnel = new HashMap<>();
        List<String> workEmpNos = null;
        if (personnelLists != null && !personnelLists.isEmpty()) {
            workEmpNos = personnelLists.stream().map(WorkPersonnel::getTemporaryEmpNo).collect(Collectors.toList());
            //位置信息点
            Map<String, HttpTagPosVO> posVOMap = bindCardCached.hGetEmpNoBindPos(workEmpNos);
            //作业人员信息
            List<WorkPersonnelVO> personnelVOList = new ArrayList<>();
            personnelLists.stream().forEach(pl -> {
                HttpTagPosVO httpTagPosVO = posVOMap.get(pl.getTemporaryEmpNo());
                WorkPersonnelVO personnelVO = new WorkPersonnelVO();
                BeanUtil.copyProperties(pl, personnelVO);
                personnelVO.setTagPos(httpTagPosVO);
                personnelVOList.add(personnelVO);
//                nowPersonnel.put(pl.getTemporaryEmpNo(), pl);
            });
            jobVO.setPersonnel(personnelVOList);
            jobLinkCountVO.setWorkerNum(personnelVOList.size());
        }
        Date startTime = job.getStartTime();
        Date endTime = job.getEndTime();
        //作业区域房间信息
        Room room = roomMapper.selectOne(Wrappers.<Room>lambdaQuery().eq(Room::getRoomNo, job.getRoomNo()));
        //作业区域场景信息
        Scene scene = sceneMapper.selectOne(Wrappers.<Scene>lambdaQuery().eq(Scene::getSceneName, job.getWorkArea()));
        if (room != null) {
            jobVO.setRoom(room);
        }
        if (scene != null) {
            jobVO.setScene(scene);
        }
        //负责人联系方式
        String directorId = job.getDirectorId();
        if (StringUtils.isNotBlank(directorId)) {
            Personnel personnel = personnelMapper.selectOne(Wrappers.<Personnel>lambdaQuery().eq(Personnel::getDirectorId, directorId));
            if (personnel != null) {
                jobVO.setDirectorPhone(personnel.getPhone());
            }
        }
        jobLinkCountVO.setJob(jobVO);
        //当天进出厂区
//        Date date = new Date();
//        String startTime = DateUtils.getStartOfDay2(date);
//        String endTime = DateUtils.getEndOfDay2(date);
//        Map<String, Long> map = cardReportMapper.groupCountByType(startTime, endTime);
//        jobLinkCountVO.setInWorkerNum(map.get("enterCount"));
//        jobLinkCountVO.setOutWorkerNum(map.get("getOutCount"));
        //获取所有绑卡记录
        Map<String, String> bindMap = redisCached.hgetAll(RedisConstant.BIND_CARD_REDIS_KEY);
        //获取所有用工号绑卡的绑卡信息
        List<BindCardDTO> bindCardList = new ArrayList<>();
        bindMap.forEach((key, value) -> {
            if (!key.startsWith(RedisConstant.BIND_CARD_REDIS_EMP_NO_KEY)) {
                return;
            }
            BindCardDTO bindCard = JSON.parseObject(value, BindCardDTO.class);
            bindCardList.add(bindCard);
        });
        //所有绑卡的人员工号
        List<String> empNos = bindCardList.stream().map(BindCardDTO::getEmpNo).collect(Collectors.toList());
        //入人员数
        Long inWorkerNum = 0L;
        //出人员数
        Long outWorkerNum = 0L;
        //超员人数
        Integer exceedWorkerNum = 0;
        //缺员人数（只计算当前工单的作业人员是否缺少）
        Integer lackWorkerNum = 0;
        //滞留人数
        Integer retentionWorkerNum = 0;
        //异常统计（只计算当前工单的作业人员是否异常）
        Integer unusualWorkerNum = 0;
        //工单范围内目前的人数
        Integer nowJobNum = 0;
        //获取所有绑卡的作业人员的最新位置
        Map<String, HttpTagPosVO> tagPosVOMap = bindCardCached.hGetEmpNoBindPos(empNos);
        int size = personnelLists.size();
        if (tagPosVOMap != null) {
            String roomPoint = null;
            if (room != null) {
                roomPoint = room.getRoomPoint();
            }
            long currentTimeMillis = System.currentTimeMillis();
            List<HighRise> highRises = highRiseMapper.selectList(Wrappers.<HighRise>lambdaQuery().orderByAsc(HighRise::getRiseMin));
            Iterator<Map.Entry<String, HttpTagPosVO>> iterator = tagPosVOMap.entrySet().iterator();
            while (iterator.hasNext()) {
                Map.Entry<String, HttpTagPosVO> next = iterator.next();
                //工号
                String key = next.getKey();
                //最新位置点
                HttpTagPosVO value = next.getValue();
                double[] pos = value.getPos();
                //定位时间
                long time = value.getTime();
                //判断该位置是否在当前作业工单的区域内
                boolean judgePos = FindMediaDevUtils.judgeScenePos(highRises, roomPoint, pos);
                //三分钟没推送位置数据代表离线，只计算在线的设备
                if (currentTimeMillis - time < 1000 * 60 * 3) {
                    if (judgePos) {
                        inWorkerNum += 1;
                    } else {
                        outWorkerNum += 1;
                    }
                }
                //当前工单人员
                if (!CollectionUtils.isEmpty(workEmpNos)) {
                    if (workEmpNos.contains(key)) {
                        //异常和缺员只统计当前工单
                        if (time < startTime.getTime()) {
                            //定位时间没有更新，证明人员位置异常和缺员
                            unusualWorkerNum += 1;
                            lackWorkerNum += 1;
                            continue;
                        } else {
                            //缺员人数只统计当前工单作业人员且在工单时间范围内
                            if (!judgePos && endTime != null && time < endTime.getTime()) {
                                lackWorkerNum += 1;
                            }
                        }
                    }
                }
                if (judgePos) {
                    if (endTime != null) {
                        if (currentTimeMillis > endTime.getTime()) {
                            //如果作业工单结束，并且作业区域内还有人员滞留，则滞留人数+1
                            retentionWorkerNum += 1;
                            continue;
                        }
                        if (currentTimeMillis > startTime.getTime() && currentTimeMillis < endTime.getTime()) {
                            //工单还未结束并且在房间内的人
                            nowJobNum += 1;
                        }
                    }
                }
            }
        } else {
            //异常只统计当前工单
            unusualWorkerNum = size;
        }
        if (nowJobNum > size) {
            exceedWorkerNum = nowJobNum - size;
        }
        jobLinkCountVO.setInWorkerNum(inWorkerNum);
        jobLinkCountVO.setOutWorkerNum(outWorkerNum);
        jobLinkCountVO.setExceedWorkerNum(exceedWorkerNum);
        jobLinkCountVO.setLackWorkerNum(lackWorkerNum);
        jobLinkCountVO.setRetentionWorkerNum(retentionWorkerNum);
        jobLinkCountVO.setUnusualWorkerNum(unusualWorkerNum);
        return JsonResult.success(jobLinkCountVO);
    }

//    public JsonResult<JobLinkCountVO> queryJobLinkCount(Long jobId) {
//        Job job = jobMapper.selectById(jobId);
//        if (job == null) {
//            return JsonResult.success();
//        }
//        JobLinkCountVO jobLinkCountVO = new JobLinkCountVO();
//        List<WorkPersonnel> personnelLists = jobPersonnelMapper.selectJobIdByPersonnelList(jobId);
//        JobVO jobVO = new JobVO();
//        BeanUtil.copyProperties(job, jobVO);
//        //实际作业人员信息
////        Map<String, WorkPersonnel> nowPersonnel = new HashMap<>();
//        if (personnelLists != null && !personnelLists.isEmpty()) {
//            List<String> empNos = personnelLists.stream().map(WorkPersonnel::getTemporaryEmpNo).collect(Collectors.toList());
//            //位置信息点
//            Map<String, HttpTagPosVO> posVOMap = bindCardCached.hGetEmpNoBindPos(empNos);
//            //作业人员信息
//            List<WorkPersonnelVO> personnelVOList = new ArrayList<>();
//            personnelLists.stream().forEach(pl -> {
//                HttpTagPosVO httpTagPosVO = posVOMap.get(pl.getTemporaryEmpNo());
//                WorkPersonnelVO personnelVO = new WorkPersonnelVO();
//                BeanUtil.copyProperties(pl, personnelVO);
//                personnelVO.setTagPos(httpTagPosVO);
//                personnelVOList.add(personnelVO);
////                nowPersonnel.put(pl.getTemporaryEmpNo(), pl);
//            });
//            jobVO.setPersonnel(personnelVOList);
//            jobLinkCountVO.setWorkerNum(personnelVOList.size());
//        }
//        //作业区域场景信息
//        Scene scene = sceneMapper.selectOne(Wrappers.<Scene>lambdaQuery().eq(Scene::getSceneName, job.getWorkArea()));
//        if (scene != null) {
//            jobVO.setScene(scene);
//        }
//        //负责人联系方式
//        String directorId = job.getDirectorId();
//        if (StringUtils.isNotBlank(directorId)) {
//            Personnel personnel = personnelMapper.selectOne(Wrappers.<Personnel>lambdaQuery().eq(Personnel::getDirectorId, directorId));
//            if (personnel != null) {
//                jobVO.setDirectorPhone(personnel.getPhone());
//            }
//        }
//        jobLinkCountVO.setJob(jobVO);
//        //当天进出厂区
//        Date date = new Date();
//        String startTime = DateUtils.getStartOfDay2(date);
//        String endTime = DateUtils.getEndOfDay2(date);
//        Map<String, Long> map = cardReportMapper.groupCountByType(startTime, endTime);
//        jobLinkCountVO.setInWorkerNum(map.get("enterCount"));
//        jobLinkCountVO.setOutWorkerNum(map.get("getOutCount"));
//        //获取所有绑卡记录
//        Map<String, String> bindMap = redisCached.hgetAll(RedisConstant.BIND_CARD_REDIS_KEY);
//        //获取所有用工号绑卡的绑卡信息
//        List<BindCardDTO> bindCardList = new ArrayList<>();
//        bindMap.forEach((key, value) -> {
//            if (!key.startsWith(RedisConstant.BIND_CARD_REDIS_EMP_NO_KEY)) {
//                return;
//            }
//            BindCardDTO bindCard = JSON.parseObject(value, BindCardDTO.class);
//            bindCardList.add(bindCard);
//        });
//        List<String> empNos = bindCardList.stream().map(BindCardDTO::getEmpNo).collect(Collectors.toList());
//        //超员人数
//        Integer exceedWorkerNum = 0;
//        //缺员人数
//        Integer lackWorkerNum = 0;
//        //滞留人数
//        Integer retentionWorkerNum = 0;
//        //异常统计
//        Integer unusualWorkerNum = 0;
//        //工单范围内目前的人数
//        Integer nowJobNum = 0;
//        //获取所有还绑卡的作业人员的最新位置
//        Map<String, HttpTagPosVO> tagPosVOMap = bindCardCached.hGetEmpNoBindPos(empNos);
//        if (tagPosVOMap != null) {
//            String scenePoint = null;
//            if (scene != null) {
//                scenePoint = scene.getScenePoint();
//            }
//            List<HighRise> highRises = highRiseMapper.selectList(Wrappers.<HighRise>lambdaQuery().orderByAsc(HighRise::getRiseMin));
//            Iterator<Map.Entry<String, HttpTagPosVO>> iterator = tagPosVOMap.entrySet().iterator();
//            while (iterator.hasNext()) {
//                Map.Entry<String, HttpTagPosVO> next = iterator.next();
//                //工号
//                String key = next.getKey();
//                //最新位置点
//                HttpTagPosVO value = next.getValue();
//                double[] pos = value.getPos();
//                //定位时间
//                long time = value.getTime();
//                if (time < job.getStartTime().getTime()) {
//                    //定位时间没有更新，证明人员位置异常
//                    unusualWorkerNum += 1;
//                    continue;
//                }
//                //判断该位置是否在当前作业工单的区域内
//                boolean judgePos = FindMediaDevUtils.judgeScenePos(highRises, scenePoint, pos);
//                if (time > job.getEndTime().getTime() && judgePos) {
//                    //如果作业工单结束，并且作业区域内还有人员滞留，则滞留人数+1
//                    retentionWorkerNum += 1;
//                    continue;
//                }
//                if (time > job.getStartTime().getTime() && time < job.getEndTime().getTime()) {
//                    //工单还未结束
//                    nowJobNum += 1;
//                }
//            }
//        } else {
//            unusualWorkerNum = empNos.size();
//        }
//        int size = personnelLists.size();
//        if (nowJobNum > size) {
//            exceedWorkerNum = nowJobNum - size;
//        }
//        if (nowJobNum < size) {
//            lackWorkerNum = size - nowJobNum;
//        }
//        //异常
//        jobLinkCountVO.setExceedWorkerNum(exceedWorkerNum);
//        jobLinkCountVO.setLackWorkerNum(lackWorkerNum);
//        jobLinkCountVO.setRetentionWorkerNum(retentionWorkerNum);
//        jobLinkCountVO.setUnusualWorkerNum(unusualWorkerNum);
//        return JsonResult.success(jobLinkCountVO);
//    }

    /**
     * 获取该作业工单下面作业人员的报警信息
     *
     * @param jobId
     * @return
     */
    @Override
    public JsonResult<List<AlarmReportVO>> queryJobAlarmList(Long jobId) {
        Job job = jobMapper.selectById(jobId);
        if (job == null) {
            return JsonResult.success();
        }
        List<WorkPersonnel> personnelLists = jobPersonnelMapper.selectJobIdByPersonnelList(jobId);
        if (personnelLists.isEmpty()) {
            return JsonResult.success();
        }
        //获取作业人员绑卡信息
        List<BindCardDTO> bindCards = bindCardCached.hmGetBindCardInfo(personnelLists.stream()
                .map(WorkPersonnel::getTemporaryEmpNo).collect(Collectors.toList()));
        if (CollectionUtils.isEmpty(bindCards)) {
            return JsonResult.success();
        }
        List<String> deviceNames = bindCards.stream().map(BindCardDTO::getDeviceName).collect(Collectors.toList());
        List<AlarmReportVO> alarmReportVOS = alarmReportMapper.batchAlarmReportDetail(Map.of("startTime", job.getStartTime(),
                "endTime", job.getEndTime(), "list", deviceNames));
        if (CollectionUtils.isEmpty(alarmReportVOS)) {
            alarmReportVOS = new ArrayList<>();
        } else {
            for (AlarmReportVO alarmReportVO : alarmReportVOS) {
                alarmReportVO.setCameraIds(pathCached.smemsPathDevice(alarmReportVO.getPathId()));
            }
        }
        return JsonResult.success(alarmReportVOS);
    }

    /**
     * 获取安全运行天数起始时间
     *
     * @return java.lang.String
     * @author ljx
     * @time 2024/9/13 20:30
     */
    @Override
    public String getSafetyDayTime() {
        String result = "";
        String str = redisCached.get(RedisConstant.SAFE_DAYS_BEGIN_TIME);
        if (StringUtils.isNotBlank(str)) {
            Date date = JSONObject.parseObject(str, Date.class);
            if (date != null) {
                result = DateUtils.dateToStr(date);
            }
        }
        return result;
    }

    /**
     * 获取设备统计
     *
     * @return java.util.Map<java.lang.Integer, java.lang.Long>
     * @author ljx
     * @time 2024/6/11 17:21
     */
    private Map<Integer, Long> getDeviceStatistics() {
        Map<Integer, Long> deviceTypeMap = new LinkedHashMap<>();
        List<LocationDevice> locationDevices = locationDeviceCached.hgetAllLocationDeviceInfo();
        Map<Integer, Long> typeCountMap = locationDevices.stream().collect(Collectors.groupingBy(LocationDevice::getDeviceType, Collectors.counting()));
        if (!CollectionUtils.isEmpty(typeCountMap)) {
            deviceTypeMap.putAll(typeCountMap);
        }
        List<Long> ids = Arrays.stream(cvrIds.split(",")).map(Long::parseLong).collect(Collectors.toList());
        //视频设备，需要过滤掉cvr两个设备
        Long count = deviceMapper.selectCount(Wrappers.<Device>lambdaQuery().notIn(Device::getId, ids));
        if (count != null) {
            deviceTypeMap.put(NumericalConstants.FOUR, count);
        }
        return deviceTypeMap;
    }

}
